﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.IO;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Reflection;

namespace SimpleMessaging
{
    //Modified for Big Endian!!!
    public abstract class SimpleMessage
    {
        protected ushort Header;
        protected ushort Length;
        protected byte[] Payload;
        protected ushort CRC;

        protected abstract byte[] AssemblePayload();
        public abstract void FromByteArray(byte[] DataIn);

        private void CalculateCRC()
        {
            CRC MyCRC = new CRC();

            MyCRC.CRCStart(0xFFFF);
            MyCRC.Step(Header);
            MyCRC.Step((byte)(Length>>8));
            MyCRC.Step((byte)(Length));

            MyCRC.Step(Payload);

            this.CRC = MyCRC.CRCVal;
        }

        public byte[] ToByteArray()
        {
            Payload = AssemblePayload();

            if (Payload != null)
            {
                this.Header = 0xAAAA;
                this.Length = (ushort)Payload.Length;
                this.CalculateCRC();

                MemoryStream MyMsg = new MemoryStream();
                BinaryWriter BW = new BinaryWriter(MyMsg);

                
                BW.Write(this.Header);
                BW.Write((byte)(this.Length>>8));
                BW.Write((byte)(this.Length));
                BW.Write(this.Payload);
                BW.Write((byte)(this.CRC>>8));
                BW.Write((byte)(this.CRC));

                byte[] eli;
                eli = MyMsg.ToArray();

                return MyMsg.ToArray();
            }
            {
                return null;
            }

        }

        public SimpleMessage()
        {
            Header = 0x0;
            Length = 0;
            Payload = null;
            CRC = 0;
        }


    }
        
    
    public class StructMap
    {

        public static object EndianFlip(object oObject)
        {
            string sFieldType;

            Type tyObject = oObject.GetType();

            FieldInfo[] miMembers;
            miMembers = tyObject.GetFields();

            for (int Looper = miMembers.GetLowerBound(0);
                   Looper <= miMembers.GetUpperBound(0);
                   Looper++)
            {
              
                sFieldType = miMembers[Looper].FieldType.FullName;
                if ((String.Compare(sFieldType, "System.UInt16", true) == 0))
                {
                    ushort tmpUShort;
                    tmpUShort = (ushort)miMembers[Looper].GetValue(oObject);
                    tmpUShort = (ushort)(((tmpUShort & 0x00ff) << 8) +
                          ((tmpUShort & 0xff00) >> 8));
                    miMembers[Looper].SetValue(oObject, tmpUShort);
                }
                else if (String.Compare(sFieldType, "System.Int16", true) == 0)
                {
                    short tmpUShort;
                    tmpUShort = (short)miMembers[Looper].GetValue(oObject);
                    tmpUShort = (short)(((tmpUShort & 0x00ff) << 8) +
                          ((tmpUShort & 0xff00) >> 8));
                    miMembers[Looper].SetValue(oObject, tmpUShort);

                }
                else
                    if (String.Compare(sFieldType, "System.UInt32", true) == 0)
                    {
                        uint tmpInt;
                        tmpInt = (uint)miMembers[Looper].GetValue(oObject);
                        tmpInt = (uint)(((tmpInt & 0x000000ff) << 24) +
                                          ((tmpInt & 0x0000ff00) << 8) +
                                          ((tmpInt & 0x00ff0000) >> 8) +
                                          ((tmpInt & 0xff000000) >> 24));
                        miMembers[Looper].SetValue(oObject, tmpInt);
                    }
            }

            return (oObject);

        }
        public static byte[] StructToBytes(object anything)
        {
            anything = EndianFlip(anything);
            byte[] rawdata = new byte[Marshal.SizeOf(anything)];
            GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
            Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
            handle.Free();
            return rawdata;
        }

        public static T BytesToStruct<T>(byte[] DataIn)
        {
            if (DataIn.Length != Marshal.SizeOf(typeof(T)))
            {
                throw new Exception("DataIn array size of " + DataIn.Length + " does not match size of struct " + typeof(T).ToString() +
                    "with size of " + Marshal.SizeOf(typeof(T)) + ".");
            }
            byte[] buffer = DataIn;
            GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));

            temp = (T)EndianFlip(temp);
            handle.Free();
            return temp;
        }
    }

    public class GlobalMessages
    {
        public class MessageCodes
        {
            public const byte MSG_UNKNOWN_MESSAGE = 0x01;
            public const byte MSG_GET_FIRMWARE_VERSION = 0x02;
            public const byte MSG_SOFTWARE_RESET = 0x03;
            public const byte MSG_BAD_CRC = 0x04;
            public const byte MSG_INVALID_LENGTH = 0x05;
            public const byte MSG_STRING = 0x06;
            public const byte MSG_CHANGE_SYSTEM_STATE = 0x07;
            public const byte MSG_REPORT_FIRMWARE_VERSION = 0x08;
        }

        public class GetFirmwareMessage : SimpleMessage
        {
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            public struct MessageStructure
            {
                public byte MsgCode;
                public byte Major;
                public byte Minor;
            }

            public MessageStructure MsgStruct;

            public GetFirmwareMessage()
            {
                MsgStruct = new MessageStructure();
                
                this.MsgStruct.MsgCode = GlobalMessages.MessageCodes.MSG_GET_FIRMWARE_VERSION;
                this.MsgStruct.Minor = 0;
                this.MsgStruct.Major = 0;
            }

            public GetFirmwareMessage(byte[] DataIn)
            {
                MsgStruct = StructMap.BytesToStruct<MessageStructure>(DataIn);
            }

            protected override byte[] AssemblePayload()
            {
                return StructMap.StructToBytes(MsgStruct);
            }

            public override void FromByteArray(byte[] DataIn)
            {
                MsgStruct = StructMap.BytesToStruct<MessageStructure>(DataIn);
            }
        }

        public class SetSystemStateMessage : SimpleMessage
        {
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            public struct MessageStructure
            {
                public byte MsgCode;
                public byte NewSystemState;
            }

            public MessageStructure MsgStruct;

            public SetSystemStateMessage()
            {
                MsgStruct = new MessageStructure();
                this.MsgStruct.MsgCode = GlobalMessages.MessageCodes.MSG_CHANGE_SYSTEM_STATE;
            }

            public SetSystemStateMessage(byte[] DataIn)
            {
                MsgStruct = StructMap.BytesToStruct<MessageStructure>(DataIn);
            }

            protected override byte[] AssemblePayload()
            {
                return StructMap.StructToBytes(MsgStruct);
            }

            public override void FromByteArray(byte[] DataIn)
            {
                MsgStruct = StructMap.BytesToStruct<MessageStructure>(DataIn);
            }
        }

        public class SoftwareResetMessage : SimpleMessage
        {
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            public struct MessageStructure
            {
                public byte MsgCode;
            }

            public MessageStructure MsgStruct;

            public SoftwareResetMessage()
            {
                MsgStruct = new MessageStructure();
                this.MsgStruct.MsgCode = GlobalMessages.MessageCodes.MSG_SOFTWARE_RESET;
            }

            public SoftwareResetMessage(byte [] DataIn)
            {
                MsgStruct = StructMap.BytesToStruct<MessageStructure>(DataIn);
            }

            protected override byte[] AssemblePayload()
            {
                return StructMap.StructToBytes(MsgStruct);
            }

            public override void FromByteArray(byte[] DataIn)
            {
                MsgStruct = StructMap.BytesToStruct<MessageStructure>(DataIn);
            }
        }

    }

}